using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using TD.SandDock;
using System.Xml;

namespace Woz
{
    public partial class MainForm : Form
    {
        /// <summary>
        /// private constructor does some basic initialization
        /// </summary>
        private MainForm()
        {
            this.mToolListFromXml = new LinkedList<string>();
        }

        /// <summary>
        /// gets the single instance of this form
        /// </summary>
        public static MainForm Instance
        {
            get
            {
                if (sInstance == null)
                {
                    sInstance = new MainForm();
                }

                return sInstance;
            }
        }

        /// <summary>
        /// adds all the user interface elements to the application
        /// </summary>
        public void BuildUserInterface()
        {
            // initialize all our components
            this.InitializeComponent();

            // initialize the project file name
            this.mProjectFileName = "";

            // initialize the last action before save
            this.mLastActionBeforeSave = null;

            // add the standard menu and menu strip
            this.Controls.Add(Woz.InterfaceManager.Instance.MenuStrips["standard"]);
            this.MainMenuStrip = Woz.InterfaceManager.Instance.MenuStrips["standard"];

            // register all our special delegates
            this.RegisterSpecialDelegates();

            // read the toolbox xml
            this.ReadToolboxFileXml();

            // add the canvas to the user interface
            this.AddCanvas();

            // add the toolstrips to the toolstrip container
            this.AddToolStrips();

            // add the screen slider to the user interface
            this.AddScreenSlider();

            // create the edit screen transitions canvas
            this.CreateEditScreenTransitionsCanvas();

            // create the run screens form
            this.CreateRunScreensControls();

            // add the toolbox to the user interface
            this.AddToolbox();

            // register all our callbacks
            this.RegisterCallbacks();
            
            // invoke the zoom changed callback
            this.cZoomComboBoxChanged(this, EventArgs.Empty);

            // switch to the design screens mode
            //this.SwitchMode(this.mButtonDesignScreens);

            // do some extra initialization for the toolbox window
            this.mWindowToolbox.TabImage = Woz.ResourceManager.Instance.Images[Woz.InterfaceManager.Instance.Buttons["Toolbox"].Background];

            // do some extra initialization for the screens window
            this.mWindowScreens.TabImage = Woz.ResourceManager.Instance.Images[Woz.InterfaceManager.Instance.Buttons["Screens"].Background];

            // update the project file name
            this.UpdateProjectFilename();

            // create an instance of the status progress form
            this.mStatusProgressForm = new Woz.StatusProgressForm();

            // create an instance of the propagate last change form
            this.mPLCForm = new Woz.PLCForm();
        }

        /// <summary>
        /// performs a series of tasks to complete applications initialization process
        /// </summary>
        public void FinalizeInitialization()
        {
            // create three different stacks in the action manager for our three different modes
            Woz.ActionManager.Instance.AddStack(Woz.StaticVariables.Instance.DesignScreens, true);
            Woz.ActionManager.Instance.AddStack(Woz.StaticVariables.Instance.EditScreenTransitions, false);
            Woz.ActionManager.Instance.AddStack(Woz.StaticVariables.Instance.RunScreens, false);

            // initialize the modes
            this.mModes = new Woz.Modes();
            this.mModes.AddMode(new Mode(Woz.StaticVariables.Instance.DesignScreens, this.mButtonDesignScreens));
            this.mModes.AddMode(new Mode(Woz.StaticVariables.Instance.EditScreenTransitions, this.mButtonEST));
            this.mModes.AddMode(new Mode(Woz.StaticVariables.Instance.RunScreens, this.mButtonRunScreens));

            // initialize the array of windows
            this.mWindows = new Woz.MainForm.Window[2];
            this.mWindows[0].Control = this.mWindowToolbox;
            this.mWindows[0].State = Woz.MainForm.DockableWindowState.Opened;
            this.mWindows[0].Width = 120;
            this.mWindows[1].Control = this.mWindowScreens;
            this.mWindows[1].State = Woz.MainForm.DockableWindowState.Opened;
            this.mWindows[1].Width = 156;

            // select the pen tool if we have it
            // selecting it in the toolbox will eventually propogate to the client and then to the canvas
            if (this.mCanvas.Tools.ContainsKey("Pen"))
            {
                this.mToolbox.SelectTool(this.mCanvas.Tools["Pen"]);
            }

            // or just select the first thing in the list
            else
            {
                Dictionary<string, Woz.Tool>.Enumerator enumerator = this.mCanvas.Tools.GetEnumerator();
                if (enumerator.MoveNext())
                {
                    this.mToolbox.SelectTool(enumerator.Current.Value);
                }
            }

            // switch to the design screens mode
            this.SwitchMode(this.mModes[Woz.StaticVariables.Instance.DesignScreens]);
        }

        /// <summary>
        /// toggles between run mode and the previous mode
        /// </summary>
        public void ToggleRunMode()
        {
            this.SwitchMode((this.mCurrentMode == this.mModes[Woz.StaticVariables.Instance.RunScreens]) ?
                        this.mPreviousMode : this.mModes[Woz.StaticVariables.Instance.RunScreens]);
        }

        /// <summary>
        /// switch to the mode with the given name
        /// </summary>
        /// <param name="pModeName">the mode to switch to</param>
        public void SwitchMode(string pModeName)
        {
            this.SwitchMode(this.mModes[pModeName]);
        }

        /// <summary>
        /// reset everything
        /// </summary>
        private void Reset()
        {
            // reset the action manager
            Woz.ActionManager.Instance.Reset();

            // reset the screen manager
            Woz.ScreenManager.Instance.Reset();

            // reset the canvas
            this.mCanvas.Reset();

            // reset the ui
            this.mScreenSlider.Reset();

            // reset the edit screen transitions canvas
            this.mEditScreenTransitionsCanvas.Reset();
        }

        /// <summary>
        /// switches the current mode to the given mode
        /// </summary>
        /// <param name="pMode">the new mode to switch to</param>
        private void SwitchMode(Woz.Mode pMode)
        {
            // make sure this is a new mode that we are trying to switch to
            if (pMode == this.mCurrentMode)
            {
                // this is the same mode so just leave
                return;
            }

            // save the previous mode
            this.mPreviousMode = this.mCurrentMode;

            // save the new mode 
            this.mCurrentMode = pMode;

            // first close down the mode that were leaving
            {
                // check if we have a previous mode that we can possibly handle
                if (this.mPreviousMode != null)
                {
                    // check if were leaving the design screens mode                
                    if (this.mPreviousMode.Name == Woz.StaticVariables.Instance.DesignScreens)
                    {
                        // merely call our function to handle this
                        this.LeaveDesignScreensMode();
                    }

                    else if (this.mPreviousMode.Name == Woz.StaticVariables.Instance.EditScreenTransitions)
                    {
                        // merely call our function to handle this
                        this.LeaveEditScreenTransitionsMode();
                    }

                    // or check to see if were leaving run screens mode
                    else if (this.mPreviousMode.Name == Woz.StaticVariables.Instance.RunScreens)
                    {
                        // merely call our function to handle this
                        this.LeaveRunScreensMode();
                    }
                }
            }

            // uncheck all the mode buttons
            this.mModes.UnCheckAllModeButtons();

            // check the new current mode button
            this.mCurrentMode.Button.Checked = true;

            // set the stack in the action manager to the one corresponding to this mode
            Woz.ActionManager.Instance.CurrentStack = this.mCurrentMode.Name;

            // now initialize the mode that were entering
            {
                // check to see if this is the design screens mode
                if (this.mCurrentMode.Name == Woz.StaticVariables.Instance.DesignScreens)
                {
                    // merely call our function to handle this
                    this.EnterDesignScreensMode();
                }

                // check to see if this is the edit screen transitions mode
                else if (this.mCurrentMode.Name == Woz.StaticVariables.Instance.EditScreenTransitions)
                {
                    // merely call our function to handle this
                    this.EnterEditScreenTransitionsMode();
                }

                // check to see if this is the run screens mode
                else if (this.mCurrentMode.Name == Woz.StaticVariables.Instance.RunScreens)
                {
                    // merely call our function to handle this
                    this.EnterRunScreensMode();
                }
            }

            // invoke this to update any ui elements upon switching into this new mode
            this.cActionDone("", "");
        }

        private void EnterDesignScreensMode()
        {
            // put the canvas back in the main form
            this.mPanelContent.Controls.Add(this.mCanvas);

            // enable the screen thumbnail slider woz button
            Woz.InterfaceManager.Instance.Buttons["Screens"].Enabled = true;

            // update the toolbox and screens windows
            foreach (Woz.MainForm.Window window in this.mWindows)
            {
                switch (window.State)
                {
                    case Woz.MainForm.DockableWindowState.Opened:
                        {
                            window.Control.OpenDocked();
                            break;
                        }
                    case Woz.MainForm.DockableWindowState.Collapsed:
                        {
                            window.Control.Open();
                            window.Control.Collapsed = true;
                            break;
                        }
                    case Woz.MainForm.DockableWindowState.Closed:
                        {
                            window.Control.Close();
                            break;
                        }
                }
            }

            // enable the zoom combobox
            if (Woz.InterfaceManager.Instance.ComboBoxes.ContainsKey("Zoom"))
            {
                Woz.InterfaceManager.Instance.ComboBoxes["Zoom"].Enabled = true;
            }

            // update the zoom on the canvas
            this.cZoomComboBoxChanged(this, EventArgs.Empty);

            // invoke this callback which will update any toolstrips this tool is using
            this.cSelectedToolChanged(this, this.mCanvas.CurrentTool);
        }

        private void LeaveDesignScreensMode()
        {
            // save the states of the windows
            for (int i = 0; i < this.mWindows.Length; i++)
            {
                if (this.mWindows[i].Control.IsOpen)
                {
                    this.mWindows[i].State = Woz.MainForm.DockableWindowState.Opened;
                }

                else if (this.mWindows[i].Control.Collapsed)
                {
                    this.mWindows[i].State = Woz.MainForm.DockableWindowState.Collapsed;
                }

                else
                {
                    this.mWindows[i].State = Woz.MainForm.DockableWindowState.Closed;
                }
            }

            // check if were going into run screens mode
            if (this.mCurrentMode.Name == Woz.StaticVariables.Instance.RunScreens)
            {
                // we are leaving this mode so set the zoom on the canvas to fit
                this.mCanvas.ZoomFit();
            }
        }

        private Woz.Tool mEditScreenTransitionsSavedTool;
        private void EnterEditScreenTransitionsMode()
        {
            // set the current tool to nothing
            this.mEditScreenTransitionsSavedTool = this.mCanvas.CurrentTool;
            this.mCanvas.CurrentTool = null;

            // disable the select all button
            Woz.InterfaceManager.Instance.Buttons["Select All"].Enabled = false;

            // hide all the toolstrips currently residing in the toolstrip container except for the standard
            foreach (ToolStrip toolStrip in this.mToolStripContainer.TopToolStripPanel.Controls)
            {
                // skip over the standard toolbar
                if (toolStrip.Name == "standard")
                {
                    continue;
                }

                // make this toolstrip invisible
                toolStrip.Visible = false;
            }

            // remove the canvas 
            this.mPanelContent.Controls.Remove(this.mCanvas);

            // disable the screen thumbnail slider woz button
            Woz.InterfaceManager.Instance.Buttons["Screens"].Enabled = false;

            // hide the toolbox and screens window
            this.mWindowToolbox.Close();
            this.mWindowScreens.Close();

            // disable the zoom combobox
            if (Woz.InterfaceManager.Instance.ComboBoxes.ContainsKey("Zoom"))
            {
                Woz.InterfaceManager.Instance.ComboBoxes["Zoom"].Enabled = false;
            }

            // add the edit screen transitions canvas to the form
            this.mPanelContent.Controls.Add(this.mEditScreenTransitionsCanvas);

            // add any new screen thumbnails to the edit screen transitions canvas
            this.mEditScreenTransitionsCanvas.RetrieveScreensFromScreenManager();

            // enter
            this.mEditScreenTransitionsCanvas.EnterEditScreenTransitions();
        }

        private void LeaveEditScreenTransitionsMode()
        {
            // leave
            this.mEditScreenTransitionsCanvas.LeaveEditScreenTransitions();

            // enable the select all button
            Woz.InterfaceManager.Instance.Buttons["Select All"].Enabled = true;

            // put the tool back
            this.mCanvas.CurrentTool = this.mEditScreenTransitionsSavedTool;

            // remove the edit screen transitions canvas to the form
            this.mPanelContent.Controls.Remove(this.mEditScreenTransitionsCanvas);
        }

        private void EnterRunScreensMode()
        {
            // add the canvas to the run screens
            this.mRunScreensForm.Controls.Add(this.mCanvas);

            // and add the navigation bar to the canvas
            this.mCanvas.Controls.Add(this.mRunScreensNavigationBar);

            // save the current screen into the run screens form
            this.mRunScreensForm.Tag = Woz.ScreenManager.Instance.SelectedScreen;

            // save the current tool into the navigation bar
            this.mRunScreensNavigationBar.Tag = this.mCanvas.CurrentTool;

            // set the current tool to nothing
            this.mCanvas.CurrentTool = null;

            // now set the screen on the canvas to be the first screen
            //Woz.ScreenManager.Instance.SelectedScreen = Woz.ScreenManager.Instance.Screens[0];

            this.mRunScreensNavigationBar.InitializeScreens();

            // show the run screens mode
            this.mRunScreensForm.Show();
        }

        private void LeaveRunScreensMode()
        {
            // remove the navigation bar to the canvas
            this.mCanvas.Controls.Remove(this.mRunScreensNavigationBar);

            // set the tool back on the canvas
            this.mCanvas.CurrentTool = (Woz.Tool)this.mRunScreensNavigationBar.Tag;

            // close the run screens
            this.mRunScreensForm.Hide();

            // set the current screen on the canvas
            Woz.ScreenManager.Instance.SelectedScreen = (Woz.Screen)this.mRunScreensForm.Tag;
        }

        /// <summary>
        /// adds the canvas to the user interface
        /// </summary>
        private void AddCanvas()
        {
            // first create it
            this.mCanvas = new Woz.Canvas();

            // tell it to fill
            this.mCanvas.Dock = DockStyle.Fill;

            // add it to the content panel
            this.mPanelContent.Controls.Add(this.mCanvas);
        }

        /// <summary>
        /// adds all the toolstrips to the toolstrip container including all the toolstrips found in the tools
        /// </summary>
        private void AddToolStrips()
        {
            // loop through all the tools
            foreach (Woz.Tool tool in this.mCanvas.Tools.Values)
            {
                // loop through all the toolstrip names of this tool
                foreach (string toolStripName in tool.ToolStripNames)
                {
                    // check to see if a toolstrip with this name exists in the interface manager
                    if (Woz.InterfaceManager.Instance.ToolStrips.ContainsKey(toolStripName))
                    {
                        // add this toolstrip to the toolstrip container
                        this.mToolStripContainer.TopToolStripPanel.Controls.Add(Woz.InterfaceManager.Instance.ToolStrips[toolStripName]);
                    }
                }
            }

            // make sure there is a standard toolstrip before adding it
            if (Woz.InterfaceManager.Instance.ToolStrips.ContainsKey("standard"))
            {
                // add the standard toolstrip to the end of the list
                this.mToolStripContainer.TopToolStripPanel.Controls.Add(Woz.InterfaceManager.Instance.ToolStrips["standard"]);
            }
        }

        /// <summary>
        /// adds the screen slider to the user interface
        /// </summary>
        private void AddScreenSlider()
        {
            // first create it
            this.mScreenSlider = new Woz.ScreenThumbnailSlider();

            // resize the container
            this.mWindowScreens.Parent.Width = Woz.StaticVariables.Instance.ThumbnailImageSize.Width + this.mScreenSlider.Padding.Horizontal * 2 + 35;

            // add it to the content panel
            //this.mWindowScreens.Controls.Add(this.mScreenSlider);
            this.mPanelSlider.Controls.Add(this.mScreenSlider);

            // tell it to dock fill
            this.mScreenSlider.Dock = DockStyle.Fill;

            // handle the screen changed event
            //this.mScreenSlider.SelectionChanged += this.OnSelectedScreenChanged;

            // now initialize it
            this.mScreenSlider.RetrieveScreensFromScreenManager();
        }

        /// <summary>
        /// adds the edit screen transitions canvas to ourselves
        /// </summary>
        private void CreateEditScreenTransitionsCanvas()
        {
            // first instantiate the canvas
            this.mEditScreenTransitionsCanvas = new Woz.EditScreenTransitionsCanvas();

            // initialize its dock
            this.mEditScreenTransitionsCanvas.Dock = DockStyle.Fill;
        }

        private void CreateRunScreensControls()
        {
            // get into the run screens mode
            this.mRunScreensForm = new Form();
            this.mRunScreensForm.FormBorderStyle = FormBorderStyle.None;
            this.mRunScreensForm.KeyPreview = true;
            this.mRunScreensForm.KeyDown += new KeyEventHandler(this.cPreviewKeyDown);
            this.mRunScreensForm.ShowInTaskbar = false;
            this.mRunScreensForm.TopMost = true;
            this.mRunScreensForm.WindowState = FormWindowState.Maximized;

            // create the run screens navigation control
            this.mRunScreensNavigationBar = new Woz.RunScreensNavBar();
            this.mRunScreensNavigationBar.Top = System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height - this.mRunScreensNavigationBar.Height - 5;
        }

        /// <summary>
        /// adds the toolbox to the user interface
        /// </summary>
        private void AddToolbox()
        {
            // well use this array when getting the tool names from the canvas
            LinkedList<string> completeToolList = new LinkedList<string>();

            // create the toolbox
            this.mToolbox = new Woz.Toolbox();

            // dock the toolbox
            this.mToolbox.Dock = DockStyle.Fill;

            // add the tools to the toolbox

            // build a list of all the tools in the canvas
            foreach (Woz.Tool tool in this.mCanvas.Tools.Values)
            {
                completeToolList.AddLast(tool.Name);
            }

            // add all the tools to the toolbox
            foreach (string toolName in this.mToolListFromXml)
            {
                // make sure this tool exists
                if (!this.mCanvas.Tools.ContainsKey(toolName))
                {
                    continue;
                }

                // it does exist so add this tool
                this.mToolbox.AddTool(this.mCanvas.Tools[toolName]);

                // and remove it from the complete list
                completeToolList.Remove(toolName);
            }

            // now loop through whatevers left of the tools and add them
            foreach (string toolName in completeToolList)
            {
                // make sure this tool exists
                if (!this.mCanvas.Tools.ContainsKey(toolName))
                {
                    continue;
                }

                // it does exist so add this tool
                this.mToolbox.AddTool(this.mCanvas.Tools[toolName]);
            }

            // add the callback to the toolbox so we know when the tool changes
            this.mToolbox.SelectedToolChanged += new Woz.Toolbox.ToolChangeHandler(this.cSelectedToolChanged);
            
            // add the toolbox to the toolbox window
            this.mWindowToolbox.Controls.Add(this.mToolbox);
        }

        /// <summary>
        /// register all of the special delegates from the resource manager that everyone out there will be using
        /// </summary>
        private void RegisterSpecialDelegates()
        {
            // set the open file dialog of the resource manager
            this.mOpenFileDialog = new OpenFileDialog();
            Woz.ResourceManager.Instance.OpenFileDialogMethod = new Woz.ResourceManager.OpenFileDialogHandler(this.cOpenFileDialog);

            // set the save file dialog of the resource manager
            this.mSaveFileDialog = new SaveFileDialog();
            Woz.ResourceManager.Instance.SaveFileDialogMethod = new Woz.ResourceManager.SaveFileDialogHandler(this.cSaveFileDialog);

            // set the color dialog of the resource manager
            this.mColorDialog = new ColorDialog();
            Woz.ResourceManager.Instance.ColorDialogMethod = new Woz.ResourceManager.ColorDialogHandler(this.cColorDialog);
        }

        /// <summary>
        /// register various callbacks to various user interface objects
        /// </summary>
        private void RegisterCallbacks()
        {
            // add a callback for the resize event of this form
            this.Resize += new EventHandler(this.cResized);
            
            // add some callbacks to our windowesque menu items
            Woz.Button.ButtonHandler windowButtonCallback = new Woz.Button.ButtonHandler(this.cWindowMenuItemClicked);
            Woz.InterfaceManager.Instance.Buttons["Toolbox"].Click += windowButtonCallback;
            Woz.InterfaceManager.Instance.Buttons["Screens"].Click += windowButtonCallback;
            
            // add callback to the file menu items
            Woz.InterfaceManager.Instance.Buttons["New Project"].Click += new Woz.Button.ButtonHandler(this.cNewButtonClicked);
            Woz.InterfaceManager.Instance.Buttons["Save Project"].Click += new Woz.Button.ButtonHandler(this.cSaveButtonClicked);
            Woz.InterfaceManager.Instance.Buttons["Save Project As..."].Click += new Woz.Button.ButtonHandler(this.cSaveAsButtonClicked);
            Woz.InterfaceManager.Instance.Buttons["Open Project..."].Click += new Woz.Button.ButtonHandler(this.cOpenButtonClicked);
            Woz.InterfaceManager.Instance.Buttons["Exit"].Click += new Woz.Button.ButtonHandler(this.cExitButtonClicked);

            // add callback to the propagate menu
            Woz.InterfaceManager.Instance.Buttons["Propagate Recent Edit(s)..."].Click += new Woz.Button.ButtonHandler(this.cPropagateLastChange);

            // add callback for the help about
            Woz.InterfaceManager.Instance.Buttons["About..."].Click += new Button.ButtonHandler(this.cHelpAboutClicked);

            // add callbacks to the mode buttons
            EventHandler modeButtonCallback = new EventHandler(this.cModeButtonClicked);
            this.mButtonDesignScreens.Click += modeButtonCallback;
            this.mButtonEST.Click += modeButtonCallback;
            this.mButtonRunScreens.Click += modeButtonCallback;

            // add callbacks for the zoom
            Woz.InterfaceManager.Instance.ComboBoxes["Zoom"].DropDownStyle = ComboBoxStyle.DropDownList;
            Woz.InterfaceManager.Instance.ComboBoxes["Zoom"].SelectedIndexChanged += new EventHandler(this.cZoomComboBoxChanged);

            // subscribe to the action done events
            Woz.ActionManager.Instance.ActionRedone += new ActionManager.ActionDoneHandler(this.cActionDone);
            Woz.ActionManager.Instance.ActionUndone += new ActionManager.ActionDoneHandler(this.cActionDone);

            // add callbacks to the undo and redo buttons
            Woz.InterfaceManager.Instance.Buttons["Undo"].Click += new Woz.Button.ButtonHandler(this.cUndoButtonClick);
            Woz.InterfaceManager.Instance.Buttons["Redo"].Click += new Woz.Button.ButtonHandler(this.cRedoButtonClick);

            // make sure that key presses can be handled by the form first before any controls get to it
            this.KeyPreview = true;

            // this is the event that handles key presses before the controls get to it
            this.KeyDown += new KeyEventHandler(this.cPreviewKeyDown);

            // subscribe to the tool stylus flipped event
            Woz.Tool.StylusFlipped += new Woz.StylusFlippedEventHandler(this.cToolStylusFlipped);
        }

        /// <summary>
        /// reads the toolbox xml file and loads the tools into an ordered list
        /// </summary>
        /// <returns></returns>
        private bool ReadToolboxFileXml()
        {
            // the file xml container well use to read the button file
            XmlTextReader toolboxFile;

            // try to open this file
            try
            {
                toolboxFile = new XmlTextReader(System.IO.Path.Combine(Woz.StaticVariables.Instance.MediaFolder, "toolbox.xml"));
            }

            catch (XmlException)
            {
                // just return false from here
                return false;
            }

            // if we made it this far then this file has been successfully opened

            // proceed to try and read it
            toolboxFile.MoveToContent();
            toolboxFile.Read();
            toolboxFile.Read();

            // loop while we didnt get to the end of the widgets list
            while (toolboxFile.Name != "toolbox")
            {
                // read this tool name and add it to the list
                this.mToolListFromXml.AddLast(toolboxFile.ReadString());

                // reader will become whitespace
                toolboxFile.Read();

                // reader will become 'tool' start element
                toolboxFile.Read();
            }

            // close the file
            toolboxFile.Close();

            // send back a successful messege
            return true;
        }

        /// <summary>
        /// captures and handles key down events before they are passed on to the rest of the controls
        /// </summary>
        /// <param name="pSender">the sender of this event</param>
        /// <param name="pKeyEventArgs">the associated event args</param>
        private void cPreviewKeyDown(object pSender, KeyEventArgs pKeyEventArgs)
        {
            switch (pKeyEventArgs.KeyCode)
            {
                case Keys.Escape:
                    {
                        if (this.mCurrentMode == this.mModes[Woz.StaticVariables.Instance.RunScreens])
                        {
                            this.SwitchMode(this.mPreviousMode);
                        }
                    }
                    break;

                case Keys.F5:
                    {
                        this.ToggleRunMode();
                    }
                    break;
            }
        }

        private void cPropagateLastChangeEventHandler(object pSender, EventArgs pEventArgs)
        {
            // check if it was successful
            if (!this.mPLCForm.SuccessfullyInitialized)
            {
                return;
            }

            // now show the plc form
            if (this.mPLCForm.ShowDialog(this) == DialogResult.OK)
            {
                // get the list of screens and changes
                Woz.Screen[] screens = this.mPLCForm.Screens;
                Woz.Action[] changes = this.mPLCForm.Changes;

                // execute the propagate last change action
                if ((screens.Length > 0) && (changes.Length > 0))
                {
                    Woz.Action action = Woz.ActionManager.Instance.CreateAction(
                        typeof(Woz.PropagateLastChangeAction),
                        screens,
                        changes);
                    action.Execute();
                }
            }
        }

        private void cPropagateLastChange(Woz.Button pButton)
        {
            // first initialize the plc form
            Woz.ResourceManager.Instance.ProgressStartMethod(
                "Calculating propagateable actions...", 
                this.cPropagateLastChangeEventHandler);
            this.mPLCForm.Initialize();
            Woz.ResourceManager.Instance.ProgressEndMethod(0);
        }

        private void cNewButtonClicked(Woz.Button pButton)
        {
            if (this.ShowClosingFileDialog())
            {
                // clear everything
                this.Reset();

                // create the first screen
                Woz.ScreenManager.Instance.AddNewScreen("", false);

                // initialize the slider
                this.mScreenSlider.RetrieveScreensFromScreenManager();

                // add any new screen thumbnails to the edit screen transitions canvas
                this.mEditScreenTransitionsCanvas.RetrieveScreensFromScreenManager();

                // reset the last executed action
                this.mLastActionBeforeSave = Woz.ActionManager.Instance.LastExecutedAction;
                this.mProjectFileName = "";
                this.UpdateProjectFilename();
            }
        }

        private void SaveProject()
        {
            if (this.mProjectFileName == "")
            {
                this.SaveProjectAs();
            }
            else
            {
                Woz.ScreenManager.Instance.SaveProject(this.mProjectFileName);
            }

            // reset the last executed action
            this.mLastActionBeforeSave = Woz.ActionManager.Instance.LastExecutedAction;
            this.UpdateProjectFilename();
        }

        private void SaveProjectAs()
        {
            SaveFileDialog saveFileDialog = new SaveFileDialog();
            saveFileDialog.AddExtension = true;
            saveFileDialog.CheckPathExists = true;
            saveFileDialog.Filter = "WOZ Project (*.woz)|*.woz";
            saveFileDialog.FilterIndex = 0;
            saveFileDialog.RestoreDirectory = false;
            saveFileDialog.Title = "Save WOZ Project as...";

            if (saveFileDialog.ShowDialog() == DialogResult.OK)
            {
                this.mProjectFileName = saveFileDialog.FileName;
                Woz.ScreenManager.Instance.SaveProject(this.mProjectFileName);
            }

            // reset the last executed action
            this.mLastActionBeforeSave = Woz.ActionManager.Instance.LastExecutedAction;
            this.UpdateProjectFilename();
        }

        private void cSaveAsButtonClicked(Button pButton)
        {
            this.SaveProjectAs();
        }

        private void cSaveButtonClicked(Button pButton)
        {
            this.SaveProject();
        }

        private void cOpenButtonClicked(Button pButton)
        {
            if (this.ShowClosingFileDialog())
            {
                OpenFileDialog openFileDialog = new OpenFileDialog();
                openFileDialog.AddExtension = true;
                openFileDialog.CheckPathExists = true;
                openFileDialog.Filter = "WOZ Project (*.woz)|*.woz";
                openFileDialog.FilterIndex = 0;
                openFileDialog.RestoreDirectory = false;
                openFileDialog.Title = "Open WOZ Project";

                if (openFileDialog.ShowDialog() == DialogResult.OK)
                {
                    this.LoadProject(openFileDialog.FileName);
                }
            }
        }

        private void cExitButtonClicked(Button pButton)
        {
            if (this.ShowClosingFileDialog())
            {
                Application.Exit();
            }
        }

        public void LoadProject(string pFilename)
        {
            if (Woz.ScreenManager.Instance.PreLoadProject(pFilename))
            {
                // reset everything
                this.Reset();

                // load the project
                Woz.ScreenManager.Instance.FinishLoadingProject();

                // intialize the screen thumbnail slider
                this.mScreenSlider.RetrieveScreensFromScreenManager();

                // add any new screen thumbnails to the edit screen transitions canvas
                this.mEditScreenTransitionsCanvas.RetrieveScreensFromScreenManager();

                // set the file name
                this.mProjectFileName = pFilename;
                this.mLastActionBeforeSave = Woz.ActionManager.Instance.LastExecutedAction;
                this.UpdateProjectFilename();
            }
        }

        /// <summary>
        /// intercept the closing event to see if the user would like to save their progress
        /// </summary>
        /// <param name="pCancelEventArgs">the cancel event args</param>
        protected override void OnClosing(CancelEventArgs pCancelEventArgs)
        {
            // make sure the user wants to save
            if (!this.ShowClosingFileDialog())
            {
                pCancelEventArgs.Cancel = true;
            }

            // call the base on closing
            base.OnClosing(pCancelEventArgs);
        }

        /// <summary>
        /// makes sure that the project is saved and if the project is not saved 
        /// this function asks the user to save it and returns true if the user
        /// saved the project or wants to close without saving it returns false
        /// when the user wants to cancel the action
        /// </summary>
        /// <returns>true to continue with the closing action false to cancel</returns>
        private bool ShowClosingFileDialog()
        {
            if (Woz.ActionManager.Instance.LastExecutedAction != this.mLastActionBeforeSave)
            {
                DialogResult result = MessageBox.Show(
                    this,
                    String.Format("Do want to save the changes to {0}?",
                        (this.mProjectFileName == "") ? "Untitled1" : System.IO.Path.GetFileName(this.mProjectFileName)),
                    Application.ProductName,
                    MessageBoxButtons.YesNoCancel,
                    MessageBoxIcon.Warning,
                    MessageBoxDefaultButton.Button1);
                
                if (result == DialogResult.Yes)
                {
                    this.SaveProject();
                    return true;
                }
                else if (result == DialogResult.No)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }

            // nothing has been changed so just return true
            return true;
        }

        /// <summary>
        /// a callback for when any mode button is clicked
        /// </summary>
        /// <param name="pSender">the object that fired this event</param>
        /// <param name="pEventArgs">the event arguments</param>
        private void cModeButtonClicked(object pSender, EventArgs pEventArgs)
        {
            // cast this sender to a button and switch the mode to it
            this.SwitchMode(this.mModes[(ToolStripButton)pSender]);
        }

        private void cToolStylusFlipped(Woz.Tool pTool, Woz.StylusFlippedEventArgs pStylusFlippedEventArgs)
        {
            // make sure there exists an eraser tool
            if (!this.mCanvas.Tools.ContainsKey("Eraser"))
            {
                return;
            }

            if (pStylusFlippedEventArgs.StylusSide == StylusSide.FlatSide)
            {
                // set the last stylus flipped tool
                this.mLastStylusFlippedTool = pTool;

                // set the current tool to the eraser tool
                this.mToolbox.SelectTool(this.mCanvas.Tools["Eraser"]);
            }
            else
            {
                // set the current tool to the last stylus flipped tool
                this.mToolbox.SelectTool(this.mLastStylusFlippedTool);

                // set the last stylus flipped tool
                this.mLastStylusFlippedTool = pTool;
            }
        }

        private void cOpenFileDialog(object sender, Woz.ResourceManager.OpenFileDialogArgs e)
        {
            if(this.InvokeRequired == false)
            {
                // load the properties
                this.mOpenFileDialog.AddExtension = e.AddExtension;
                this.mOpenFileDialog.CheckFileExists = e.CheckFileExists;
                this.mOpenFileDialog.CheckPathExists = e.CheckPathExists;
                this.mOpenFileDialog.DefaultExt = e.DefaultExt;
                this.mOpenFileDialog.DereferenceLinks = e.DereferenceLinks;
                this.mOpenFileDialog.FileName = e.FileName;
                this.mOpenFileDialog.Filter = e.Filter;
                this.mOpenFileDialog.FilterIndex = e.FilterIndex;
                this.mOpenFileDialog.InitialDirectory = e.InitialDirectory;
                this.mOpenFileDialog.Multiselect = e.Multiselect;
                this.mOpenFileDialog.ReadOnlyChecked = e.ReadOnlyChecked;
                this.mOpenFileDialog.RestoreDirectory = e.RestoreDirectory;
                this.mOpenFileDialog.ShowHelp = e.ShowHelp;
                this.mOpenFileDialog.ShowReadOnly = e.ShowReadOnly;
                this.mOpenFileDialog.SupportMultiDottedExtensions = e.SupportMultiDottedExtensions;
                this.mOpenFileDialog.Title = e.Title;
                this.mOpenFileDialog.ValidateNames = e.ValidateNames;

                // now show it with the main form as the owner
                e.DialogResult = this.mOpenFileDialog.ShowDialog(this);

                // save the properties
                e.FileName = this.mOpenFileDialog.FileName;
            }
            else
            {
                this.Invoke(new Woz.ResourceManager.OpenFileDialogHandler(this.cOpenFileDialog),
                    new object[] { sender, e });
            }
        }

        private void cSaveFileDialog(object sender, Woz.ResourceManager.SaveFileDialogArgs e)
        {
            if (this.InvokeRequired == false)
            {
                // load the properties
                this.mSaveFileDialog.CheckFileExists = e.CheckFileExists;
                this.mSaveFileDialog.CheckPathExists = e.CheckPathExists;
                this.mSaveFileDialog.DefaultExt = e.DefaultExt;
                this.mSaveFileDialog.DereferenceLinks = e.DereferenceLinks;
                this.mSaveFileDialog.FileName = e.FileName;
                this.mSaveFileDialog.Filter = e.Filter;
                this.mSaveFileDialog.FilterIndex = e.FilterIndex;
                this.mSaveFileDialog.InitialDirectory = e.InitialDirectory;
                this.mSaveFileDialog.RestoreDirectory = e.RestoreDirectory;
                this.mSaveFileDialog.ShowHelp = e.ShowHelp;
                this.mSaveFileDialog.SupportMultiDottedExtensions = e.SupportMultiDottedExtensions;
                this.mSaveFileDialog.Title = e.Title;
                this.mSaveFileDialog.ValidateNames = e.ValidateNames;
                this.mSaveFileDialog.OverwritePrompt = e.OverridePrompt;

                // now show it with the main form as the owner
                e.DialogResult = this.mSaveFileDialog.ShowDialog(this);

                // save the properties
                e.FileName = this.mSaveFileDialog.FileName;
            }
            else
            {
                this.Invoke(new Woz.ResourceManager.SaveFileDialogHandler(this.cSaveFileDialog),
                    new object[] { sender, e });
            }
        }

        private void cColorDialog(object pSender, Woz.ResourceManager.ColorDialogArgs pArgs)
        {
            if (this.InvokeRequired == false)
            {
                // first set the values to the color dialog based on the passed in arguments
                this.mColorDialog.AllowFullOpen = pArgs.AllowFullOpen;
                this.mColorDialog.AnyColor = pArgs.AnyColor;
                this.mColorDialog.Color = pArgs.Color;
                this.mColorDialog.CustomColors = pArgs.CustomColors;
                this.mColorDialog.FullOpen = pArgs.FullOpen;
                this.mColorDialog.SolidColorOnly = pArgs.SolidColorOnly;

                // show the dialog
                pArgs.DialogResult = this.mColorDialog.ShowDialog();

                // save the actual color result to the arguments
                pArgs.Color = this.mColorDialog.Color;
            }
            else
            {
                this.Invoke(new Woz.ResourceManager.ColorDialogHandler(this.cColorDialog),
                    new object[] { pSender, pArgs });
            }
        }

        private void cSelectedToolChanged(object pSender, Woz.Tool pTool)
        {
            // set the current tool to the pen tool
            this.mCanvas.CurrentTool = pTool;

            // add any toolstrips this tool wants to add

            // hide all the toolstrips currently residing in the toolstrip container except for the standard
            foreach (ToolStrip toolStrip in this.mToolStripContainer.TopToolStripPanel.Controls)
            {
                // skip over the standard toolbar
                if (toolStrip.Name == "standard")
                {
                    continue;
                }

                // make this toolstrip invisible
                toolStrip.Visible = false;
            }

            // now turn on all the toolstrips belonging to this new tool
            foreach (string toolStripName in this.mCanvas.CurrentTool.ToolStripNames)
            {
                // make sure this toolstrip exists
                if (!Woz.InterfaceManager.Instance.ToolStrips.ContainsKey(toolStripName))
                {
                    continue;
                }

                // this toolstrip exists so make it visible
                Woz.InterfaceManager.Instance.ToolStrips[toolStripName].Visible = true;
            }
        }

        private void cZoomComboBoxChanged(object pSender, EventArgs pEventArgs)
        {
            string zoomText = Woz.InterfaceManager.Instance.ComboBoxes["Zoom"].Text;
            if (zoomText == "Fit")
            {
                this.mCanvas.ZoomFit();
            }
            else
            {
                this.mCanvas.Zoom = Convert.ToSingle(zoomText.Replace('%', ' ')) / 100.0f;
                Woz.InterfaceManager.Instance.ComboBoxes["Zoom"].Text = String.Format("{0}%", this.mCanvas.Zoom * 100);
            }

            // throw the focus to the toolbox window just so we dont stay selected
            this.mToolbox.Focus();
        }

        private void cUndoButtonClick(Woz.Button pButton)
        {
            // merely tell the canvas to undo
            this.mCanvas.Undo();
        }

        private void cRedoButtonClick(Woz.Button pButton)
        {
            // merely tell the canvas to redo
            this.mCanvas.Redo();
        }

        private void cWindowMenuItemClicked(Woz.Button pButton)
        {
            // get the menu item
            ToolStripMenuItem menuItem = pButton.MenuItem;

            // well populate this reference to a window then do some logic on it
            DockableWindow window = null;

            // set the window based on the name of the menu item
            if (menuItem.Text == "Toolbox")
            {
                // set the window to the toolbox window
                window = this.mWindowToolbox;
            }

            else if (menuItem.Text == "Screens")
            {
                // set the window to the screens window
                window = this.mWindowScreens;
            }

            // make sure we found a window
            if (window == null)
            {
                // we didnt so just leave this method
                return;
            }

            // we have the window now do some logic on it

            // check if its closed
            if (!window.IsOpen)
            {
                // it was so open and pin it
                window.OpenDocked();
            }

            // or check if it was collapsed
            else if (window.Collapsed)
            {
                // it was collapsed so just pop it open
                window.Open();
            }
        }

        private void cActionDone(string pNewToolName, string pOldToolName)
        {
            // update the undo and redo buttons on the interface
            Woz.InterfaceManager.Instance.Buttons["Undo"].Enabled = Woz.ActionManager.Instance.CanUndo;
            Woz.InterfaceManager.Instance.Buttons["Redo"].Enabled = Woz.ActionManager.Instance.CanRedo;

            // switch to the tool that the action was in originally
            if (this.mCanvas.CurrentTool != null)
            {
                if ((this.mCanvas.CurrentTool.Name != pNewToolName) && (pNewToolName != ""))
                {
                    this.mToolbox.SelectTool(this.mCanvas.Tools[pNewToolName]);
                }
            }

            // update the project file name
            this.UpdateProjectFilename();
        }

        private void cResized(object pSender, EventArgs pEventArgs)
        {
            // resize the dock containers
            this.mDockContainerLeft.Width = this.mWindows[0].Width;
            this.mDockContainerRight.Width = this.mWindows[1].Width;
        }

        private void cHelpAboutClicked(Woz.Button pButton)
        {
            // create a splash form and show it
            Woz.SplashForm splashForm = new Woz.SplashForm(true);
            splashForm.ShowDialog();
        }

        /// <summary>
        /// updates the project filename indicating whether the project has been saved or not
        /// </summary>
        private void UpdateProjectFilename()
        {
            // check to see if the last action before we saved has changed
            string title = ((this.mProjectFileName == "") ? "Untitled1" : System.IO.Path.GetFileName(this.mProjectFileName));
            if (Woz.ActionManager.Instance.LastExecutedAction != this.mLastActionBeforeSave)
            {
                this.Text = String.Format("{0}* - {1}", title, Application.ProductName);
            }
            else
            {
                this.Text = String.Format("{0} - {1}", title, Application.ProductName);
            }
        }

        private enum DockableWindowState { Opened, Collapsed, Closed };

        private struct Window
        {
            public DockableWindow Control;
            public Woz.MainForm.DockableWindowState State;
            public int Width;
        }

        /// <summary>
        /// the single instance of this class
        /// </summary>
        private static MainForm sInstance;

        /// <summary>
        /// keeps track of the last action
        /// </summary>
        private Woz.Action mLastActionBeforeSave;

        /// <summary>
        /// keeps track of the current project file name
        /// </summary>
        private string mProjectFileName;

        /// <summary>
        /// represents the current application mode
        /// </summary>
        private Mode mCurrentMode;

        /// <summary>
        /// represents the application mode prior to the current one
        /// </summary>
        private Mode mPreviousMode;

        /// <summary>
        /// the canvas to draw on
        /// </summary>
        private Woz.Canvas mCanvas;

        /// <summary>
        /// canvas for the edit screen transitions mode
        /// </summary>
        private Woz.EditScreenTransitionsCanvas mEditScreenTransitionsCanvas;

        /// <summary>
        /// form for the run screens mode
        /// </summary>
        private Form mRunScreensForm;

        /// <summary>
        /// navigation bar for the run screens mode
        /// </summary>
        private RunScreensNavBar mRunScreensNavigationBar;

        /// <summary>
        /// the toolbox to which all the tools will be loaded into
        /// </summary>
        private Woz.Toolbox mToolbox;

        /// <summary>
        /// the screen thumbnail slider
        /// </summary>
        private Woz.ScreenThumbnailSlider mScreenSlider;

        /// <summary>
        /// the ordered list of tools that will be read from the xml
        /// </summary>
        private LinkedList<string> mToolListFromXml;

        /// <summary>
        /// open file dialog that can be used by whoever
        /// </summary>
        private OpenFileDialog mOpenFileDialog;

        /// <summary>
        /// save file dialog that can be used by whoever
        /// </summary>
        private SaveFileDialog mSaveFileDialog;

        /// <summary>
        /// color dialog that can be used by whoever
        /// </summary>
        private ColorDialog mColorDialog;

        /// <summary>
        /// the progress status dialog form
        /// </summary>
        private Woz.StatusProgressForm mStatusProgressForm;

        /// <summary>
        /// the last tool the canvas was using before the stylus was flipped
        /// </summary>
        private Woz.Tool mLastStylusFlippedTool;

        /// <summary>
        /// represents the three different woz modes
        /// </summary>
        private Woz.Modes mModes;

        /// <summary>
        /// array of windows that holds the toolbox and screens windows
        /// </summary>
        private Woz.MainForm.Window[] mWindows;

        /// <summary>
        /// represents the propagate last change form
        /// </summary>
        private Woz.PLCForm mPLCForm;
    }
}